package cn.yj.wx.pay.utils;

import cn.yj.wx.pay.emums.TradeState;
import cn.yj.wx.pay.enrity.NoticeBody;
import cn.yj.wx.pay.enrity.NoticeBodyResource;
import cn.yj.common.exception.PayException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;

/**
 * @author 永健
 * @since 2021-09-23 10:01
 */
public class WxPayUtils {

    private static final int KEY_LENGTH_BYTE = 32;

    private static final int TAG_LENGTH_BIT = 128;

    private static final String patten = "yyyy-MM-dd'T'HH:mm:ssXXX";

    /**
     * 微信 v3 sign 签名
     * <p>
     * <p>
     * JSAPI为例  toBeSignStr 拼接请移步：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
     *
     * @param toBeSignStr - 待签名字符串 参数拼接进来: 签名串一共有四行，每一行为一个参数。行尾以\n（换行符，ASCII编码值为0x0A）结束，包括最后一行。如果参数本身以\n结束，也需要附加一个\n
     *                    - appid    小程序或者公众号ID
     *                    - timeStamp 当前的时间，其他详见时间戳规则。示例值：1414561699
     *                    - nonceStr  - 随机字符串，不长于32位。
     *                    - package   --JSAPI下单接口返回的prepay_id参数值  prepay_id=***
     *                    签名，使用字段appId、timeStamp、nonceStr、package
     * @param privateKey
     *
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public static String v3Sign(String toBeSignStr, PrivateKey privateKey) throws NoSuchAlgorithmException,
            InvalidKeyException, SignatureException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(toBeSignStr.getBytes());
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    public static String v3Sign(
            String appid, String timeStamp, String nonceStr, String packageStr, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        StringBuffer toBeSignStr = new StringBuffer();
        toBeSignStr.append(appid).append("\n");
        toBeSignStr.append(timeStamp).append("\n");
        toBeSignStr.append(nonceStr).append("\n");
        toBeSignStr.append(packageStr).append("\n");
        sign.initSign(privateKey);
        sign.update(toBeSignStr.toString().getBytes());
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    /**
     * date格式转为为iso8601方法
     * 下单是指定该笔订单未支付有效时间
     * @param date
     * @return
     */
    public static String parseDate(Date date) {
        return new SimpleDateFormat(patten).format(date);
    }

    /**
     * iso8601格式转为为date方法
     * @param date
     * @return
     */
    public static String parseIso806Date(String date) {
        return date.replaceAll("T"," ").replaceAll("\\+08:00","");
    }


    /**
     * 回调通知/数据解密
     * @param v3apiKey
     * @return
     * @throws GeneralSecurityException
     */
    public static JSONObject decryptNoticeResult(NoticeBodyResource resource, String v3apiKey)
            throws GeneralSecurityException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(aesKey(v3apiKey), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, resource.getNonce().getBytes());
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(resource.getAssociated_data().getBytes());
            return JSON.parseObject(new String(cipher.doFinal(Base64.getDecoder().decode(resource.getCiphertext())), StandardCharsets.UTF_8));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }


    /**
     * 对支付回调结果进行校验是否支付成功了
     * @param noticeBody
     * @return
     */
    public static JSONObject checkPayNotice(NoticeBody noticeBody,String v3apiKey) throws GeneralSecurityException {
        if (!noticeBody.isSuccess()) {
            throw new PayException("支付失败");
        }
        JSONObject result = decryptNoticeResult(noticeBody.getResource(), v3apiKey);
        System.out.println("解密结果：" + result);

        // 交易状态
        String tradeState = result.getString("trade_state");

        // 交易成功
        if (!TradeState.SUCCESS.name().equals(tradeState)) {
            throw new PayException("支付失败");
        }
        return result;
    }

    private static byte[] aesKey(String v3apiKey){
        if (v3apiKey.getBytes().length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key，长度必须为32个字节");
        }
        return v3apiKey.getBytes();
    }

    public static void main(String[] args) {
        String s = parseDate(new Date());
        String s1 = parseIso806Date(s);
        System.out.println(s);
        System.out.println(s1);
    }
}
